﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using Microsoft.Data.Entity.Infrastructure;
using System.Diagnostics;

namespace Microsoft.Data.Entity
{

    public static class DbContextExtensions
    {

        public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class
        {
            var context = set.GetService<DbContext>();

            var entityType = context.Model.FindEntityType(typeof(TEntity));
            var key = entityType.FindPrimaryKey();

            var entries = context.ChangeTracker.Entries<TEntity>();

            var i = 0;
            foreach (var property in key.Properties)
            {
                var currentKeyValue = keyValues.ElementAtOrDefault(i);
                entries = entries.Where(e => e.Property(property.Name).CurrentValue == currentKeyValue);
                i++;
            }

            var entry = entries.FirstOrDefault();
            if (entry != null)
            {
                // Return the local object if it exists.
                return entry.Entity;
            }

            // TODO: Build the real LINQ Expression
            // set.Where(x => x.Id == keyValues[0]);
            var defaultKeyName = typeof(TEntity).GetProperties().FirstOrDefault(x => x.Name == typeof(TEntity).Name + "Id")?.Name ?? "Id";
            var parameter = Expression.Parameter(typeof(TEntity), "x");
            var query = set.Where((Expression<Func<TEntity, bool>>)
                Expression.Lambda(
                    Expression.Equal(
                        Expression.Property(parameter, defaultKeyName),
                        Expression.Constant(keyValues[0])),
                    parameter));

            // Look in the database
            return query.FirstOrDefault();
        }

        public static void InsertOrUpdate<TDbContext, TEntity>(this TDbContext context, TEntity entity) where TDbContext : DbContext where TEntity : class
        {
            var key = typeof(TEntity).GetRuntimeProperties().FirstOrDefault(x => x.GetCustomAttribute<KeyAttribute>() != null);
            if (key == null)
            {
                throw new ArgumentException($"The entity {nameof(TEntity)} doen't have a primary key property.");
            }
            var keyValue = key.GetValue(entity);
            if (keyValue == null || string.Equals(keyValue, default(TEntity)))
            {
                context.Entry(entity).State = EntityState.Added;
            }
            else
            {
                var tryFind = context.Set<TEntity>().Any(x => object.Equals(key.GetValue(x), keyValue));
                if (tryFind)
                {
                    context.Entry(entity).State = EntityState.Modified;
                }
                else
                {
                    context.Entry(entity).State = EntityState.Added;
                }
            }
        }

    }

}